package com.ckf.serene.service.impl;

import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.ckf.serene.config.AliPayConfig;
import com.ckf.serene.domain.AliPayParam;
import com.ckf.serene.domain.AliPayRefundParam;
import com.ckf.serene.exception.CustomException;
import com.ckf.serene.service.AliPayService;
import com.ckf.serene.utils.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 支付宝服务实现类
 *
 * @author serence
 * @date 2021/11/6 23:39
 */
@Slf4j
@Service
public class AliPayServiceImpl implements AliPayService {

    /**
     * 设置支付宝请求
     *
     * @return
     */
    public CertAlipayRequest setUpAliPayAsk() {
        // 初始化
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        // 支付宝网关
        certAlipayRequest.setServerUrl(AliPayConfig.SERVER_URL);
        // 应用id
        certAlipayRequest.setAppId(AliPayConfig.APP_ID);
        // 应用私钥
        certAlipayRequest.setPrivateKey(AliPayConfig.APP_PRIVATE_KEY);
        try {
            // 应用公钥证书路径，下载后保存位置的绝对路径
            certAlipayRequest.setCertPath(String.valueOf(ResourceUtils.getFile(AliPayConfig.APPLICATION_PUBLIC_KEY_CERT)));
            // 支付宝公钥证书路径，下载后保存位置的绝对路径
            certAlipayRequest.setAlipayPublicCertPath(String.valueOf(ResourceUtils.getFile(AliPayConfig.AliPay_PUBLIC_CERT)));
            // 支付宝根证书路径，下载后保存位置的绝对路径
            certAlipayRequest.setRootCertPath(String.valueOf(ResourceUtils.getFile(AliPayConfig.ROOT_CERT_PATH)));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 设置签名类型
        certAlipayRequest.setSignType(AliPayConfig.sign_type);
        // 设置请求格式，固定值json
        certAlipayRequest.setFormat(AliPayConfig.FORMAT);
        // 设置编码格式
        certAlipayRequest.setCharset(AliPayConfig.charset);
        return certAlipayRequest;
    }


    /**
     * 支付宝APP支付
     *
     * @param aliPayParam 支付宝参数类
     * @return
     */
    @Override
    public String aliPayAPPToPay(AliPayParam aliPayParam) {
        try {
            // 设置支付宝请求
            CertAlipayRequest certAlipayRequest = setUpAliPayAsk();
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            // 实例化具体API对应的request类，类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay（app 支付接口）
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            // 设置业务参数
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            // 商户订单号，商户自定义，需保证在商户端不重复，如：20200612000001
            String outTradeNo = "商户单号";
            model.setOutTradeNo(outTradeNo);
            //订单标题
            model.setSubject(aliPayParam.getSubject());
            // 销售产品码，固定值 QUICK_MSECURITY_PAY
            model.setProductCode(AliPayConfig.product_code_app);
            // 订单金额，精确到小数点后两位 **/
            model.setTotalAmount(aliPayParam.getTotalAmount().toString());
            // 订单描述
            model.setBody(aliPayParam.getBody());
            // 业务扩展参数
            //ExtendParams extendParams = new ExtendParams();
            // 系统商编号，填写服务商的PID用于获取返佣，返佣参数传值前提：传值账号需要签约返佣协议，用于isv商户。
            //extendParams.setSysServiceProviderId("2088511****07846");
            // 花呗分期参数传值前提：必须有该接口花呗收款准入条件，且需签约花呗分期
            // 指定可选期数，只支持3/6/12期，还款期数越长手续费越高
            // extendParams.setHbFqNum("3");
            // 指定花呗分期手续费承担方式，手续费可以由用户全承担（该值为0），也可以商户全承担（该值为100），但不可以共同承担，即不可取0和100外的其他值。 **/
            //extendParams.setHbFqSellerPercent("0");
            //model.setExtendParams(extendParams);

            // 将业务参数至request中
            request.setBizModel(model);
            // 异步通知地址，以http或者https开头的，商户外网可以post访问的异步地址，用于接收支付宝返回的支付结果，如果未收到该通知可参考该文档进行确认：https://opensupport.alipay.com/support/helpcenter/193/201602475759
            request.setNotifyUrl(AliPayConfig.notify_url + AliPayConfig.AliPay_APP_PAY_Callback);
            //第三方调用（服务商模式），传值app_auth_token后，会收款至授权token对应商家账号，如何获传值app_auth_token请参考文档：https://opensupport.alipay.com/support/helpcenter/79/201602494631
            //request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if (response.isSuccess()) {
                return response.getBody();
            } else {
                return response.getSubMsg();
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    /**
     * 查询支付宝订单
     *
     * @param aliPayParam 支付宝参数类
     * @return
     */
    @Override
    public AlipayTradeQueryResponse queryAliPayOrder(AliPayParam aliPayParam) {
        try {
            //设置支付宝请求
            CertAlipayRequest certAlipayRequest = setUpAliPayAsk();
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            if (StringUtils.isNotEmpty(aliPayParam.getOutTradeNo())) {
                bizContent.put("out_trade_no", aliPayParam.getOutTradeNo());
            }
            if (StringUtils.isNotEmpty(aliPayParam.getTradeNo())) {
                bizContent.put("trade_no", aliPayParam.getTradeNo());
            }
            request.setBizContent(bizContent.toString());
            AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
            if (response.isSuccess()) {
                System.out.println("调用成功");
                return response;
            } else {
                System.out.println("调用失败");
            }

        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        throw new CustomException("系统繁忙，请稍后重试");
    }


    /**
     * 支付宝退款
     *
     * @param payRefund 支付宝退款参数类
     * @return
     */
    @Override
    public AlipayTradeRefundResponse aliPayRefund(AliPayRefundParam payRefund) {
        //设置支付宝请求
        CertAlipayRequest certAlipayRequest = setUpAliPayAsk();
        try {
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            // 实例化具体API对应的request类，类名称和接口名称对应,当前调用接口名称：alipay.trade.refund（统一收单交易退款接口）
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            // 设置业务参数
            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
            // 支付接口传入的商户订单号。如：2020061601290011200000140004
            model.setOutTradeNo(payRefund.getOutTradeNo());
            // 退款金额，退款总金额不能大于该笔订单支付最大金额 **/
            model.setRefundAmount(payRefund.getRefundAmount().toString());
            // 异步通知/查询接口返回的支付宝交易号，如：2020061622001473951448314322
            model.setTradeNo(payRefund.getTradeNo());
            //随机数  不是全额退款，部分退款必须使用该参数，同一个订单，不同的out_request_no代表部分退款
            //String outRequestNo = RandomStringUtils.randomAlphanumeric(13);
            // 如需部分退款，则此参数必传，且每次请求不能重复，如：202006230001
            model.setOutRequestNo(payRefund.getRefundNo());
            model.setRefundReason(payRefund.getRefundReason());
            request.setBizModel(model);
            //第三方调用（服务商模式），传值app_auth_token后，会收款至授权token对应商家账号，如何获传值app_auth_token请参考文档：https://opensupport.alipay.com/support/helpcenter/79/201602494631
            //request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");
            // 通过alipayClient调用API，获得对应的response类
            AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
            // 获取接口调用结果，如果调用失败，可根据返回错误信息到该文档寻找排查方案：https://opensupport.alipay.com/support/helpcenter/101
            return response;
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        throw new CustomException("系统繁忙，请稍后重试");
    }


    /**
     * 查询支付宝退款订单
     *
     * @param payRefund 支付宝退款参数类
     * @return
     */
    @Override
    public AlipayTradeFastpayRefundQueryResponse aliPayRefundQuery(AliPayRefundParam payRefund) {
        try {
            //设置支付宝请求
            CertAlipayRequest certAlipayRequest = setUpAliPayAsk();
            AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
            com.alibaba.fastjson.JSONObject bizContent = new com.alibaba.fastjson.JSONObject();
            if (StringUtils.isNotEmpty(payRefund.getOutTradeNo())) {
                bizContent.put("outTrade_no", payRefund.getOutTradeNo());
            }
            if (StringUtils.isNotEmpty(payRefund.getTradeNo())) {
                bizContent.put("trade_no", payRefund.getTradeNo());
            }
            if (StringUtils.isNotEmpty(payRefund.getRefundNo())) {
                bizContent.put("out_request_no", payRefund.getRefundNo());
            }
            request.setBizContent(bizContent.toString());
            AlipayTradeFastpayRefundQueryResponse response = alipayClient.certificateExecute(request);
            if (response.isSuccess()) {
                return response;
            } else {
                System.out.println("查订单失败");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        throw new CustomException("系统繁忙，请稍后重试");
    }


    /**
     * 支付宝支付、退款成功回调
     *
     * @param request HttpServlet 请求
     */
    @Override
    public Integer aliPayNotify(HttpServletRequest request) {
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        log.info("requestParams: {}", requestParams);
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        // 商户订单号
        //String out_trade_no = request.getParameter("out_trade_no");
        // 订单内容
        //String orderContent = request.getParameter("body");
        // 交易状态 支付成功:TRADE_SUCCESS,  交易关闭:TRADE_CLOSED, 交易完成:TRADE_FINISHED
        String tradeStatus = request.getParameter("trade_status");
        //签名验证(对支付宝返回的数据验证，确定是支付宝返回的)
        try {
            //调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AliPayConfig.AliPay_PUBLIC_KEY, AliPayConfig.charset, AliPayConfig.sign_type);
            //验证签约是否成功
            if (signVerified) {
                log.info("验签成功");
                log.info("交易状态: {}", tradeStatus);
                if (tradeStatus.equals("TRADE_SUCCESS")) {
                    System.out.println("交易状态为TRADE_SUCCESS:支付成功");
                }
                if (tradeStatus.equals("TRADE_CLOSED")) {
                    System.out.println("交易状态为TRADE_CLOSED:交易关闭");
                }
                //支付宝交易号
                String tradeNo = params.get("trade_no");
                log.info("tradeNo: {}", tradeNo);
                //商户单号
                String outTradeNo = params.get("out_trade_no");
                log.info("outTradeNo: {}", outTradeNo);

                //商户业务号 这里作为退款单号
                String outBizNo = params.get("out_biz_no");
                //退款总金额
                String refundFee = params.get("refund_fee");
                //交易退款时间
                String gmtRefund = params.get("gmt_refund");
                if (StringUtils.isNotEmpty(refundFee) && StringUtils.isNotEmpty(gmtRefund)) {
                    System.out.println("退款成功回调通知");
                    //退款业务逻辑处理
                } else {
                    System.out.println("支付成功回调通知");
                    //支付业务逻辑处理
                }
            } else {
                log.info("数字签名验证失败: {}", "500");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("异常状态: {} ", e.getMessage());
        }
        return Constants.SUCCESS;
    }

}
